#ifndef __SLOTHTTPD_AIO_LINUX_AIO_OPERATION_HPP__
#define __SLOTHTTPD_AIO_LINUX_AIO_OPERATION_HPP__

#include <slothttpd/aio/aio_linux.h>
#include <slothttpd/types.hpp>
#include <slothttpd/file.hpp>

#include <boost/asio.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/noncopyable.hpp>
#include <boost/function.hpp>

namespace slothttpd {
namespace aio {

// NOTE: looks like linux aio is almost useless for us...
//
// cancelling async operations can always fail. if it cannot be cancelled in destructor,
// thread must be blocked until operation is completed
//
// the same is about linux_aio_context
class linux_aio_context;

class base_async_operation :
        //public wait_queue,
        public boost::noncopyable,
        public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink> >
{
    friend class linux_aio_context;
    base_async_operation(base_async_operation &&rvalue) = delete;
public:
    base_async_operation();
    bool cancel();
    void complete(std::size_t length, const boost::system::error_code &err);
    void wait();
    bool is_pending() const;
    virtual ~base_async_operation();
    
    typedef boost::function<void(std::size_t, const boost::system::error_code&)> operation_handler;
    
protected:
    void submit_cmd(linux_aio_context &ctx, file_ptr f, char *buffer, std::size_t size, off64_t off,
                    operation_handler &handler, int cmd);
    
    linux_aio_context      *io_ctx;
    operation_handler       on_complete_handler;
    struct iocb             io_block;
};

class async_read :
        public base_async_operation
{
public:
    void submit(linux_aio_context &ctx, file_ptr f, char *buffer, std::size_t size,
                off64_t off, operation_handler &handler);
};

class async_write : 
        public base_async_operation
{
public:
    void submit(linux_aio_context &ctx, file_ptr f, const char *buffer, std::size_t size,
                off64_t off, operation_handler &handler);
};

// do NOT fork if this is used
// only worker process can use that
class linux_aio_context
{
    friend class base_async_operation;
public:
    static const unsigned default_number_of_events;
    
    linux_aio_context(boost::asio::io_service &ios, unsigned nr_events = default_number_of_events);
    ~linux_aio_context() noexcept;
    void wait_all();
    void cancel_all();
    bool has_operations_pending() const;
    
private:
    void process_operations();
    void wait_for(base_async_operation &wait_op);
    bool submit(base_async_operation &op);
    bool cancel(base_async_operation &op);
    void complete_op(struct io_event &ev);
    void wait_for(base_async_operation *wait_this);
    void stop();
    
    void post_read_eventfd();
    void handle_read_eventfd(std::size_t bytes_transferred, const boost::system::error_code &err);
    
private:
    typedef boost::intrusive::list<base_async_operation, boost::intrusive::constant_time_size<false> >
        operation_list;
    
    aio_context_t   ctx_id;
    operation_list  ops;
    boost::asio::io_service &ios_;
    boost::asio::posix::stream_descriptor eventfd_stream_;
    char event_buffer_[sizeof(eventfd_t)];
    bool eventfd_stream_read_pending_;
};

}
}

#endif
